package com.reyco1.medusa.display
{
	import com.reyco1.medusa.core.IDisposable;
	import com.reyco1.medusa.debug.Debugger;
	import com.reyco1.medusa.debug.Surrogate;
	import com.reyco1.medusa.events.DebuggerEvent;
	import com.reyco1.medusa.events.Dispatcher;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.utils.Dictionary;
	
	public class SmartSprite extends Sprite implements IDisposable
	{
		private var _id:String;
		private var _isRendering:Boolean;
		private var _numListeners:int = 0;
		private var _listeners:Array  = [];
		
		private var surrogate:Surrogate;
		
		public function SmartSprite(classIdentifyer:String = null)
		{
			super();	
			_id = classIdentifyer == null ? name : classIdentifyer;
			addEventListener(Event.ADDED_TO_STAGE, 	   handleAdded);
			addEventListener(Event.REMOVED_FROM_STAGE, handleRemoved);
			surrogate = new Surrogate(this);
		}
		
		/* public */
		
		public function log(outputItem:*, type:String = null):void
		{
			Debugger.log(outputItem, this, type);
		}
		
		public function startRender():void
		{
			if( !_isRendering && !hasEventListener(Event.ENTER_FRAME) )
			{
				_isRendering = true;
				addEventListener(Event.ENTER_FRAME, handleEnterFrame);
			}
		}
		
		public function stopRender():void
		{
			if(_isRendering) 
			{
				_isRendering = false;
				removeEventListener(Event.ENTER_FRAME, handleEnterFrame);
			}
		}
		
		public function move(toX:Number, toY:Number):void
		{
			x = toX;
			y = toY;
		}
		
		public function scale(value:Number):void
		{
			scaleX = scaleY = value;
		}
		
		public function removeChildByName(name:String):void
		{
			removeChild(getChildByName(name));
		}
		
		public function removeAllChildren():void
		{
			while(numChildren > 0) removeChildAt(0);
		}
		
		override public function set width(value:Number):void
		{
			super.width = value;
			dispatchEvent(new Event(Event.RESIZE));
		}
		
		override public function set height(value:Number):void
		{
			super.height = value;
			dispatchEvent(new Event(Event.RESIZE));
		}
		
		override public function set scaleX(value:Number):void
		{
			super.scaleX = value;
			dispatchEvent(new Event(Event.RESIZE));
		}
		
		override public function set scaleY(value:Number):void
		{
			super.scaleY = value;
			dispatchEvent(new Event(Event.RESIZE));
		}
		
		override public function set scaleZ(value:Number):void
		{
			super.scaleZ = value;
			dispatchEvent(new Event(Event.RESIZE));
		}
		
		override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
		{
			super.addEventListener(type, listener, useCapture, priority, useWeakReference);
			
			var capture:int = int(useCapture);
			
			if (capture in _listeners == false)
			{
				_listeners[capture] = {};
			}
			
			if (type in _listeners[capture] == false)
			{
				_listeners[capture][type] = new Dictionary(true);
			}
			
			if (listener in _listeners[capture][type] == false)
			{
				_listeners[capture][type][listener] = true;
				_numListeners++;
			}
		}
		
		override public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
		{
			super.removeEventListener(type, listener, useCapture);
			
			try 
			{
				var capture:int = int(useCapture);
				
				if (delete _listeners[capture][type][listener])
				{
					_numListeners--;
					
					var cleanObject:Boolean;
					var key:*;
					
					cleanObject = true;
					
					for (key in _listeners[capture][type])
					{
						cleanObject = false; 
						break;
					}
					
					if (cleanObject) delete _listeners[capture][type];
					
					cleanObject = true;
					
					for (key in _listeners[capture])
					{
						cleanObject = false; 
						break;
					}
					
					if (cleanObject) delete _listeners[capture];
				}				
			}
			catch(err:Error)
			{
			}
		}
		
		public function removeAllListeners(type:String = null):void 
		{
			var capture:*; 
			var etype:String;
			var listener:*;
			var types:Object;
			var listeners:Dictionary;
			
			for (capture in _listeners)
			{				
				types = _listeners[int(capture)];
				
				for (etype in types)
				{					
					if (!type || type == etype) 
					{
						listeners = types[etype];
						for (listener in listeners)
						{
							try 
							{
								super.removeEventListener(etype, listener, Boolean(capture));
							}
							catch(err:Error)
							{
							}
							
							delete listeners[listener];
							_numListeners--;
						}
						
						delete types[etype];
					}
				}
				
				if (type == null || _numListeners == 0) delete _listeners[int(capture)];
			}
		}
		
		public function output():void
		{
			log(this);
		}

		/* for override */
		
		protected function onAdded():void	{}		
		protected function onRemoved():void	{}		
		protected function onResize():void	{}
		protected function onRender():void	{}
		public	  function clear():void		{}
		
		/* handlers */
		
		protected function handleAdded(e:Event):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, handleAdded);
			addEventListener(Event.RESIZE, handleResize);
			Dispatcher.addEventListener(DebuggerEvent.COMMAND, handleCommand);
			onAdded();
		}
		
		protected function handleRemoved(e:Event):void
		{
			_isRendering = false;
			removeAllListeners();
			Dispatcher.removeEventListener(DebuggerEvent.COMMAND, handleCommand);
			onRemoved();
		}
		
		protected function handleEnterFrame(e:Event):void
		{
			onRender();
		}
		
		protected function handleResize(e:Event):void
		{
			onResize();
		}
		
		private function handleCommand(e:DebuggerEvent):void
		{
			var host:String = e.details.command.split(" ")[0];
			if(host == classID) surrogate.evaluateCommand(e.details.command);
		}
		
		/* properties */

		public function get isRendering():Boolean
		{
			return _isRendering;
		}
		
		public function get numListeners():int
		{
			return _numListeners;
		}
		
		public function get listeners():Array
		{
			return _listeners;
		}

		public function get classID():String
		{
			return _id == null ? name : _id;
		}

		public function set classID(value:String):void
		{
			_id = value;
		}

	}
}